<template>
  <div class="query_log">
    <header class="header">日志查询</header>
    <div class="control">
      <div class="search_item">
        <!-- 应用选择 -->
        <el-select v-model="listQuery.appValue" placeholder="查询应用" class="app_select">
          <el-option
            v-for="(item, index) in appList"
            :key="item.id"
            :label="item.Name"
            :value="item.id"
          >
          </el-option>
        </el-select>
        <!-- 日期时间选择 -->
        <el-date-picker
          v-model="listQuery.dateRange"
          type="datetimerange"
          :shortcuts="shortcuts"
          range-separator="至"
          start-placeholder="开始时间"
          end-placeholder="结束时间"
          class="date_select"
        >
        </el-date-picker>
      </div>
      <div class="search_item">
        <!-- 关键字搜索 -->
        <el-input class="keyWord_input" v-model="searchKeyWord" placeholder="搜索" maxLength="100" clearable> </el-input>
        <!-- 刷新按钮 -->
        <el-button type="primary" size="mini" class="refresh_btn" @click="search">
          <el-icon class="el-icon-refresh-right"></el-icon>
          <span>刷新</span>
        </el-button>
      </div>
      <div class="condition">
        <div class="filter_tag">
          <el-tag v-for="(item, index) in filterList" :key="index" class="tag_item" closable :disable-transitions="false" size="medium" @close="handleFilterTag(item, index)" @click="editFilter(item)">{{ item.title }}</el-tag>
        </div>
        <el-button type="text" size="mini" class="search_btn" ghost @click="addFilter">添加筛选</el-button>
        <el-button type="primary" size="mini" class="search_btn" @click="search">查询日志</el-button>
      </div>
    </div>
    <div class="container">
      <div class="table_group">
        <div class="log_table">
          <el-table :data="showLogList" style="" :stripe="true" class="container_table" empty-text="暂无数据">
            <el-table-column type="expand">
              <template #default="{row}">
                <div class="expand_item">
                  <div v-for="(value, key) in row" :key="key" class="key_item text-ellipsis">
                    <span class="key">{{key}}：</span>
                    <span v-if="typeof value == 'object'" class="value first_child">
                      <div v-for="(value_2nd, key_2nd) in value" :key="key_2nd" class="first_child_item">
                        <span class="second_key">{{key_2nd}}：</span>
                        <span v-if="typeof value_2nd == 'object'" class="value second_child">
                          <div v-for="(value_3rd, key_3rd) in value_2nd" :key="key_3rd" class="second_child_item">
                            <span class="third_key">{{key_3rd}}：</span>
                            <span class="third_value">{{value_3rd}}</span>
                          </div>
                        </span>
                        <span v-else class="value">{{value_2nd}}</span>
                      </div>
                    </span>
                    <span v-else class="value">{{value}}</span>
                  </div>
                </div>
              </template>
            </el-table-column>
            <el-table-column v-for="(head, hindex) in showColumns" :prop="head.key" :label="head.key" :width="head.width" :key="hindex">
              <template #default="{row}">
                <div class="text-ellipsis">
                  {{ row[head.key] }}
                </div>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>
      <el-pagination
        v-model:currentPage="pageOptions.pageNum"
        layout="total, sizes, prev, pager, next, jumper"
        :page-sizes="[20, 50 ,100]"
        :page-size="pageOptions.limit"
        :pager-count="5"
        :total="pageOptions.total"
        background
        class="page_group"
        @size-change="onSizeChange"
        @current-change="onCurrentChange"
      ></el-pagination>
      <!-- 侧栏 -->
      <div class="condition_key" :class="{open: isSpread}">
        <div class="main">
          <div class="title">选择显示字段</div>
          <div class="key_item" v-for="(item, index) in keyList" :key="index">
            <el-checkbox class="key_item_checkbox" v-model="item.checked" @change="val => keySettingChange(val, item.key)">{{item.key}}</el-checkbox>
          </div>
        </div>
        <div class="spread" @click="isSpread = !isSpread">
          <el-icon v-show="isSpread" class="el-icon-caret-left"></el-icon>
          <el-icon v-show="!isSpread" class="el-icon-caret-right"></el-icon>
        </div>
      </div>
    </div>

    <!-- 编辑筛选 -->
    <el-dialog
      v-model="isShowFilterDialog"
      :title="filterTitle"
      :width="800"
      @close="closeFilterDialog"
      custom-class="filter_dialog"
    >
      <div class="main">
        <div class="info_item key_item">
          <div class="key">
            <div class="label">字段</div>
            <el-select v-model="filterInfo.key" placeholder="字段" class="key_select">
              <el-option label="id" value="id"></el-option>
              <el-option label="appkey" value="appkey"></el-option>
            </el-select>
          </div>
          <div class="operator">
            <div class="label">运算符</div>
            <el-select v-model="filterInfo.operator" :placeholder="filterInfo.key ? '请输入运算符':'等待字段选择'" :disabled="!filterInfo.key">
              <el-option label="是" value="=="></el-option>
              <el-option label="不是" value="!="></el-option>
              <el-option label="属于" value="∈"></el-option>
              <el-option label="不属于" value="!∈"></el-option>
              <el-option label="存在" value="!!"></el-option>
              <el-option label="不存在" value="!"></el-option>
            </el-select>
          </div>
        </div>
        <template v-if="filterInfo.operator && !['!', '!!'].includes(filterInfo.operator)">
          <div class="info_item" v-if="['∈', '!∈'].includes(filterInfo.operator)">
            <div class="label">值</div>
            <div class="tag_group">
              <el-tag v-for="tag in filterInfo.valueList" :key="tag" class="tag_item" closable :disable-transitions="false" size="medium" @close="handleCloseTag(tag)">{{ tag }}</el-tag>
              <el-input v-if="inputTagVisible" ref="saveTagInput" v-model="inputTagValue" class="input-new-tag" size="mini" @keyup.enter="handleInputConfirm" @blur="handleInputConfirm" clearable></el-input>
              <el-button v-else class="button-new-tag" size="small" @click="showInputTagVisible">
                <el-icon class="el-icon-circle-plus-outline"></el-icon>
              </el-button>
            </div>
          </div>
          <div class="info_item" v-else>
            <div class="label">值</div>
            <el-input class="info_input" v-model="filterInfo.val" placeholder="请输入值" maxLength="100" clearable> </el-input>
          </div>
        </template>
        <div class="info_item switch_item">
          <el-switch v-model="filterInfo.hasTag" class="tag_switch"/>
          <div class="label">创建定制标签?</div>
        </div>
        <div class="info_item" v-show="filterInfo.hasTag">
          <div class="label">定制标签</div>
          <el-input class="info_input" v-model="filterInfo.tagValue" placeholder="请输入运算符" maxLength="16" clearable> </el-input>
        </div>
      </div>

      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeFilterDialog">取消</el-button>
          <el-button type="primary" @click="saveFilter">保存</el-button>
        </div>
      </template>
    </el-dialog>

  </div>  
</template>

<script lang="ts">
import { defineComponent, onMounted,ref,getCurrentInstance,nextTick,watchEffect,reactive,Ref, toRefs, watch} from 'vue'
/* API */
import { searchLog } from '@/api/log'
import { getAppList } from '@/api/application'
/* utils */
import ls from '@/lib/script/storage'
import dayjs from 'dayjs';
import { ElMessage } from 'element-plus';
export default defineComponent({
  setup(){
    let appList: Array<any> = ref([]); // 应用列表
    // 查询条件
    let listQuery = reactive({
      appValue: '',
      dateRange: '',
    })
    // 日期快捷选择配置
    let shortcuts = [
      {
        text: '最近一周',
        value: () => {
          const start = new Date()
          start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
          return [start, new Date()]
        },
      },
      {
        text: '最近一个月',
        value: () => {
          const start = new Date()
          start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
          return [start, new Date()]
        },
      },
      {
        text: '最近三个月',
        value: () => {
          const start = new Date()
          start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
          return [start, new Date()]
        },
      },
    ]
    let searchKeyWord = ref('') // 搜索栏关键字
    let filterList = ref([]); // 筛选器列表

    let filterTitle = ref('编辑筛选');
    let isShowFilterDialog = ref(false); // 是否显示筛选器弹窗
    let keyList = ref([]); // key列表
    let filterInfo = reactive({
      key: '', // 字段
      operator: '', // 运算符
      val: '', // 值
      valueList: [], // 值(当operator为[属于/不属于]时有值)
      hasTag: false, // 是否创建定制标签
      tagValue: '', // 定制标签名称
    })
    let inputTagVisible = ref(false) // 显示输入标签值的输入框
    let inputTagValue = ref('') // 输入标签值
    
    // 表头
    let columns = ref([
      // authInfo
      {
        key: 'addr',
        width: 60,
      },
      {
        key: 'apikey',
        width: 100,
      },
      {
        key: 'env',
        width: 70,
      },
      {
        key: 'ipAddr',
        width: 150,
      },
      {
        key: 'authInfo.name',
        width: 150,
      },
      {
        key: 'sdkVersion',
        width: 100,
      },
      {
        key: 'sessionID',
        width: 100,
      },
      {
        key: 'trackerId',
        width: 100,
      },
      {
        key: 'uin',
        width: 60,
      },
      {
        key: 'version',
        width: 100,
      },
      // breadcrumb
      {
        key: 'breadcrumb',
        width: 100,
      },
      // data
      {
        key: 'errorId',
        width: 100,
      },
      {
        key: 'ext',
        width: 60,
      },
      {
        key: 'ext1',
        width: 60,
      },
      {
        key: 'ext2',
        width: 60,
      },
      {
        key: 'level',
        width: 60,
      },
      {
        key: 'message',
        width: 100,
      },
      {
        key: 'data.name',
        width: 150,
      },
      {
        key: 'stack',
        width: 60,
      },
      {
        key: 'time',
        width: 180,
      },
      {
        key: 'type',
        width: 150,
      },
      {
        key: 'url',
        width: 500,
      },
    ]); 
    let showColumns = ref([]);
    let logList = ref([]); // 日志列表
    let showLogList = ref([]); // 日志列表

    let isSpread = ref(false); // 是否展开key设置

    let pageOptions = reactive({
      pageNum: 1,
      limit: 20,
      total: 0,
    })

    /* vnode */
    const saveTagInput: Ref<HTMLElement | null> = ref(null);

    /* methods */
    const closeFilterDialog = _ => {
      isShowFilterDialog.value = false;
    }
    // 保存筛选条件
    const saveFilter = _ => {
      let options = filterInfo;

      // 标签名
      options.title = '';
      // 判断条件
      switch (options.operator) {
        case '==': // 是
        case '!=': // 不是
          options.title = `${options.key}: ${options.val}`
          options.condition = `${options.key}${options.operator}${options.val}`
          break;
        case '!!': // 存在
        case '!': // 不存在
          options.title = `${options.operator}${options.key}`
          options.condition = `${options.operator}${options.key}`
          break;
        case '∈': // 属于
        case '!∈': // 不属于
          options.title = options.tagValue || `${options.key}: ${(options.valueList.length && JSON.stringify(options.valueList)) || ''}`;
          options.condition = `${JSON.stringify(options.valueList)}.some(val => ${options.key}.indexOf(val) >= 0)`
          break;
      }

      // 标签名
      options.title = options.tagValue || options.title;

      filterList.value.push(options)
      isShowFilterDialog.value = false;
    }
    // 移除筛选条件
    const handleFilterTag = (item, index) => {
      filterList.value.splice(index, 1)
    }
    // 新增筛选
    const addFilter = _ => {
      filterInfo.key = '';
      filterInfo.operator = '';
      filterInfo.val = '';
      filterInfo.valueList = [];
      filterInfo.hasTag = false;
      filterInfo.tagValue = '';
      filterTitle.value = '新增筛选'
      isShowFilterDialog.value = true;
    }
    // 编辑筛选
    const editFilter = item => {
      filterInfo.key = item.key;
      filterInfo.operator = item.operator;
      filterInfo.val = item.val;
      filterInfo.valueList = item.valueList;
      filterInfo.hasTag = item.hasTag;
      filterInfo.tagValue = item.tagValue;
      filterTitle.value = '编辑筛选'
      isShowFilterDialog.value = true;
    }
    // 删除标签值
    const handleCloseTag = tag => {
      filterInfo.valueList = filterInfo.valueList.filter(ele => ele != tag);
    }
    // 显示标签输入框
    const showInputTagVisible = _ => {
      inputTagValue.value = '';
      inputTagVisible.value = true;
      nextTick(() => {
        saveTagInput.value.focus()
      })
    }
    // 标签输入完成
    const handleInputConfirm = _ => {
      let tagText = inputTagValue.value;
      console.log(tagText, !!tagText);
      console.log(filterInfo, filterInfo.valueList);
      if(!!tagText && !filterInfo.valueList.some(ele => ele == tagText)) {
        filterInfo.valueList.push(inputTagValue.value)
      }
      nextTick(() => {
        inputTagVisible.value = false;
      })
    }
    // 页容量change
    const onSizeChange = val => {
      pageOptions.limit = val;
      pageOptions.pageNum = 1;
      search()
    }
    // 页码change
    const onCurrentChange = val => {
      pageOptions.pageNum = val;
      search()
    }
    // 字段设置change
    const keySettingChange = (val, key) => {
      let keyConfig = ls.getItem('monitoring_key_config') || [];
      console.log('keySettingChange', keyConfig, typeof keyConfig, Array.isArray(keyConfig));
      if(val){
        keyConfig = keyConfig.filter(ele => ele != key)
      } else {
        keyConfig.push(key)
      }
      updateShowColumns()
      ls.setItem('monitoring_key_config', keyConfig)
    }
    // 获取app列表
    const getAppInfoList = () => {
      let args = {
        pageNum: 1,
        limit: 50,
      }
      getAppList(args).then(resp => {
        let {code, data, message} = resp;
        if(code === 0) {
          appList.value = data || [];
        } else {
          ElMessage.error({message: `code ${code}; ${message}`})
        }
      })
    }
    // 获取日志请求参数
    const getLogArgs = () => {
      let args = {
        from: (pageOptions.pageNum-1) * pageOptions.limit,
        size: pageOptions.limit,
      }
      return args;
    }
    // 查询日志
    const search = async () => {
      let args = getLogArgs()
      let {code, message, data} = await searchLog(args);

      if(code === 0) {
        console.log('data.hits', data.hits);
        let hits = data.hits || [];
        logList.value.push(...hits);
        showLogList.value.push(...disposeData(hits));
        pageOptions.total = data.total.value;
      } else {
        ElMessage.error({message: `code ${code}; ${message}`})
      }
    }
    // 处理日志返回数据
    const disposeData = dataList => {
      let list = dataList.map(ele => {
        let result = {};
        let {authInfo, breadcrumb, data} = ele._source
        for (let [key, value] of Object.entries(authInfo)) { 
          if(key == 'name'){
            result['authInfo.' + key] = value;
          } else {
            result[key] = value;
          }
        }
        for (let [key, value] of Object.entries(data)) {
          if(key == 'name'){
            result['data.'+key] = value;
          } else {
            result[key] = value;
          }
        }
        result['breadcrumb'] = breadcrumb;
        return result;
      })
      console.log('list', list);
      return list

    }
    // 初始化keyList的勾选
    const initKeyList = () => {
      console.log('initKeyList');
      let keys = [];
      let keyConfig = ls.getItem('monitoring_key_config') || [];
      console.log('keyConfig', keyConfig);
      let list = columns.value.map(ele => {
        if(!keyConfig.includes(ele.key)) {
          ele.checked = true;
        }
        return ele;
      })

      keyList.value.push(...list)
      updateShowColumns()
    }
    // 更新表格表头显示
    const updateShowColumns = () => {
      showColumns.value = keyList.value.filter(ele => ele.checked)
    }
    const init = async () => {
      initKeyList();
      getAppInfoList();
      await search();
    }



    // search();
    init();

    return {
      appList,
      listQuery,
      shortcuts,
      searchKeyWord,
      filterList,
      filterTitle,
      keyList,
      isShowFilterDialog,
      filterInfo,
      inputTagVisible,
      inputTagValue,
      columns,
      showColumns,
      logList,
      showLogList,
      pageOptions,
      isSpread,

      saveTagInput,

      closeFilterDialog,
      addFilter,
      editFilter,
      saveFilter,
      handleCloseTag,
      showInputTagVisible,
      handleInputConfirm,
      onSizeChange,
      onCurrentChange,
      keySettingChange,
      search,
    }
  }
})
</script>

<style lang='less' scoped>
.query_log {
  width: 100%;
  overflow: auto;
  header.header {
    height: 40px;
    line-height: 40px;
    background-color: #fff;
    font-size: 16px;
    padding: 0 16px;
  }
  .control {
    padding: 8px 16px;
    .search_item {
      display: flex;
      align-items: center;
      margin-bottom: 10px;
      .app_select {
        margin-right: 10px;
      }
      .date_select {
        width: 300px;
        margin-right: 10px;
      }
      .keyWord_input {
        // width: 300px;
      }
      .refresh_btn {
        width: 150px;
        margin-left: 10px;
      }
    }
  }

  .container {
    width: 100%;
    padding: 0 16px;
    overflow: auto;
    position: absolute;
    left: 0;
    right: 0;
    top: 168px;
    bottom: 0;
    .table_group {
      position: absolute;
      top: 0;
      bottom: 42px;
      left: 16px;
      right: 16px;
      overflow: auto;
      .log_table {
        .container_table {
          .expand_item {
            width: 100%;
            max-width: 100%;
            padding:  0 16px 0 56px;
            .key_item {
              display: flex;
              // align-items:
              .key {
                font-weight: bold;
              }
              .value {
                &.first_child {
                  .first_child_item {
                    display: flex;
                    .second_child {
                      .second_child_item {
                        display: flex;
                        .third_value {
                          width: 600px;
                          white-space: pre-wrap;
                          word-break: break-all;
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    .page_group {
      position: absolute;
      left: 16px;
      bottom: 10px;
    }
    .condition_key {
      position: absolute;
      width: 250px;
      left: -250px;
      top: 0;
      bottom: 42px;
      // background-color: skyblue;
      background-color: #fff;
      box-shadow: 0px 0 5px 5px #eee;
      border-radius: 0 6px 6px 0;
      transition: all 0.3s;
      padding: 12px 0 12px 12px;
      z-index: 10;
      .main {
        height: 100%;
        padding-right: 12px;
        overflow: auto;
        .title {
          height: 26px;
          line-height: 26px;
          color: 333;
          font-size: 16px;
          border-bottom: 1px solid #000;
          margin-bottom: 10px;
        }
        .key_item {
          .key_item_checkbox {
            color: #8f8f8f;
          }
        }
      }
      .spread {
        position: absolute;
        width: 14px;
        height: 50px;
        line-height: 50px;
        text-align: center;
        color: #fff;
        background-color: #1e222d;
        right: -14px;
        top: 50%;
        box-shadow: 2px 0 3px 1px #eee;
        border-radius: 0 6px 6px 0;
        transition: all 0.1s;
        transform: translateY(-50%);
      }
      &.open {
        transform: translateX(100%);
        .spread {
          color: #8f8f8f;
          background-color: #fff;
        }
      }
    }
  }

  .filter_dialog {
    .main {
      .info_item {
        margin-bottom: 20px;
        .label {
          margin-bottom: 10px;
        }
        .tag_group {
          display: flex;
          flex-wrap: wrap;
          .tag_item {
            height: 32px;
            line-height: 30px;
            margin-right: 10px;
          }
          .input-new-tag {
            width: 100px;
            height: 32px;
            line-height: 32px;
            :deep(.el-input__inner) {
              height: 32px;
              line-height: 32px;
            }
          }
          .button-new-tag {
            // height: 28px;
            // padding: 6px 11px;
          }
        }
      }
      .key_item {
        display: flex;
        .key {
          flex: 1;
          padding-right: 10px;
          .key_select {
            width: 100%;
          }
        }
        .operator {
          width: 160px;
        }
      }
      .switch_item {
        display: flex;
        .tag_switch {
          margin-right: 10px;
        }
      }
    }
  }
}
</style>